package producersConsumersTests.utils;

import prod_cons.TaskContainer;

/**
 * Tester class is the class that runs and stops the threads. It uses templates
 * to get custom insertion and removal Runnables. The inserters/removers must be
 * created by the user (because they're generic). Originaly, the
 * inserters/removers are created by the test classes.
 * 
 * The class also has beforeRun and afterRun abstract methods so that the user
 * can decide what to do before and after the run - which statistics to gather
 * and such.
 * 
 * @param <INSERTER>
 *            producer type
 * @param <REMOVER>
 *            consumer type
 */
public abstract class Tester<INSERTER extends Inserter<?>, REMOVER extends Remover> {
	public Tester(INSERTER[] inserters, REMOVER[] removers, int timeToRun,
			TaskContainer container) {
		super();
		this.inserters = inserters;
		this.removers = removers;
		this.timeToRun = timeToRun;
		this.container = container;
	}

	protected INSERTER[] inserters;
	protected REMOVER[] removers;

	private final int timeToRun;
	protected final TaskContainer container;

	public final void runTest() throws InterruptedException {
		Thread[] inserterThreads = new Thread[inserters.length];
		for (int i = 0; i < inserters.length; i++) {
			inserterThreads[i] = new Thread(inserters[i]);
		}

		Thread[] removerThreads = new Thread[removers.length];
		for (int i = 0; i < removers.length; i++) {
			removerThreads[i] = new Thread(removers[i]);
		}

		this.beforeRun();

		// start the threads
		for (Thread t : inserterThreads) {
			t.start();
		}
		for (Thread t : removerThreads) {
			t.start();
		}
		Thread.sleep(timeToRun);

		System.out.println("Stopping the threads...");
		// stop the threads
		for (Inserter<?> inserter : inserters)
			inserter.stop();

		for (Remover remover : removers)
			remover.stop();

		for (Thread t : inserterThreads) {
			t.join();
		}
		for (Thread t : removerThreads) {
			t.join();
		}
		this.afterRun();
	}

	protected abstract void beforeRun();

	protected abstract void afterRun();
}
